﻿using Grow.AspNetCore.Builder;
using Grow.AspNetCore.Options;
using Grow.AspNetCore.Service;
using Grow.AspNetCore.Thread;
using Grow.Module;
using Grow.Module.AspNetCore.Service;
using Grow.Module.Builder;
using Grow.Module.Options;
using Grow.Module.Service;
using Grow.Module.Thread;
using Grow.Utils.Exceptions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;

namespace Grow.AspNetCore
{
    public class AspNetCoreStartup
    {
        private readonly ModuleBuilder _moduleBuilder;
        private readonly WebApplicationBuilder _webApplicationBuilder;
        public AspNetCoreStartup(string[]? args = null)
        {
            _moduleBuilder = new();
            _webApplicationBuilder = WebApplication.CreateBuilder(args ?? Array.Empty<string>());
        }

        public AspNetCoreStartup AddModule<TModule>() where TModule : IModule, new()
        {
            _ = _moduleBuilder.AddModule<TModule>();
            return this;
        }
        public async Task RunAsync(Action<IOptionsBind, AspNetCoreOptions> aspNetCoreOptionsAction)
        {
            AspNetCoreOptions aspNetCoreOptions = new();
            if (string.IsNullOrEmpty(aspNetCoreOptions.Store))
            {
                throw new GrowFrameworkException("aspNetCoreOptions.Store is null");
            }

            OptionsManager optionsManager = new(_webApplicationBuilder.Configuration, _webApplicationBuilder.Services);

            aspNetCoreOptionsAction(optionsManager.GetOptionsBind(), aspNetCoreOptions);

            optionsManager.AddOptions<AspNetCoreOptions>(a =>
            {
                a = aspNetCoreOptions;
            });

            BuilderManager builderManager = new();
            _ = _webApplicationBuilder.Services.AddTransient<IBuilderManager, BuilderManager>();

            _ = _moduleBuilder.AddModule<AspNetCoreModule>();
            _moduleBuilder.Load();
            _ = _webApplicationBuilder.Services.AddSingleton(_moduleBuilder);

            DependModuleBuilder dependModuleBuilder = new(_moduleBuilder);
            _ = _webApplicationBuilder.Services.AddSingleton(dependModuleBuilder);

            _ = _webApplicationBuilder.Services.AddSingleton<ICancellationTokenContext, CancellationTokenContext>();
            _ = _webApplicationBuilder.Services.AddSingleton<IServiceManager, ServiceManager>();
            _ = _webApplicationBuilder.Services.AddTransient<IServiceContext, ServiceContext>();
            _ = _webApplicationBuilder.Services.AddTransient<IApplicationServiceContext, ApplicationServiceContext>();


            ServiceContainer serviceContainer = new(_webApplicationBuilder.Services, optionsManager, builderManager);

            IEnumerable<IModule> modules = _moduleBuilder.GetModules();
            foreach (IModule module in modules)
            {
                module.AddDependModule(dependModuleBuilder);
                module.AddService(serviceContainer);
            }

            WebApplication webApplication = _webApplicationBuilder.Build();
            IServiceManager serviceManager = webApplication.Services.GetRequiredService<IServiceManager>();
            serviceManager.AddService<IApplicationBuilder>(webApplication);
            serviceManager.AddService<IEndpointRouteBuilder>(webApplication);


            IApplicationServiceContext applicationServiceContext = webApplication.Services.GetRequiredService<IApplicationServiceContext>();
            foreach (IModule module in modules)
            {
                module.UseService(applicationServiceContext);
            }
            await webApplication.RunAsync();
        }
    }
}